package ru.space_game.entities;

import java.awt.Rectangle;

import ru.space_game.core.Game;
import ru.space_game.resource.Sprite;

/**
 * An entity represents any element that appears in the game. The entity is
 * responsible for resolving collisions and movement based on a set of
 * properties defined either by subclass or externally.
 * 
 * Note that doubles are used for positions. This may seem strange given that
 * pixels locations are integers. However, using double means that an entity can
 * move a partial pixel. It doesn't of course mean that they will be display
 * half way through a pixel but allows us not lose accuracy as we move.
 * 
 * @author Kevin Glass
 */
public abstract class Entity {

	/** The current x location of this entity */
	protected float x;

	/** The current y location of this entity */
	protected float y;

	/** The current x location of this entity on the screen */
	protected int screenX;

	/** The current y location of this entity on the screen */
	protected int screenY;

	/** The sprite that represents this entity */
	protected Sprite sprite;

	/** The current speed of this entity */
	protected float speed;

	/** The current angel of this entity in degrees */
	protected float angel;

	/** The rectangle used for this entity during collisions resolution */
	private Rectangle me = new Rectangle();

	/** The rectangle used for other entities during collision resolution */
	private Rectangle him = new Rectangle();

	protected boolean visible;

	protected int miniMapColor = 0x000000;

	/**
	 * Construct a entity based on a sprite image and a location.
	 * 
	 * @param sprite
	 *            The reference to the image to be displayed for this entity
	 * @param x
	 *            The initial x location of this entity
	 * @param y
	 *            The initial y location of this entity
	 */
	protected Entity(Sprite sprite, int x, int y) {
		this.sprite = sprite;
		this.x = x;
		this.y = y;
	}

	/**
	 * Request that this entity move itself based on a certain amount of time
	 * passing.
	 * 
	 * @param delta
	 *            The amount of time that has passed in milliseconds
	 */
	public void move(long delta) {
		// update the location of the entity based on move speeds
		x += delta * speed * Math.sin(angel * Math.PI / 180) / 1000;
		y += delta * speed * Math.cos(angel * Math.PI / 180) / 1000;
	}

	/**
	 * Set the vertical speed of this entity
	 * 
	 * @param dy
	 *            The vertical speed of this entity (pixels/sec)
	 */
	public void setSpeed(float speed) {
		this.speed = speed;
	}

	/**
	 * Get the vertical speed of this entity
	 * 
	 * @return The vertical speed of this entity (pixels/sec)
	 */
	public float getSpeed() {
		return speed;
	}

	/**
	 * Draw this entity to the graphics context provided
	 */
	public void draw() {
		if (visible)
			sprite.draw(screenX, screenY, angel);
	}

	/**
	 * Do the logic associated with this entity. This method will be called
	 * periodically based on game events
	 */
	public void doLogic() {
	}

	/**
	 * Get the x location of this entity
	 * 
	 * @return The x location of this entity
	 */
	public int getX() {
		return (int) x;
	}

	/**
	 * Get the y location of this entity
	 * 
	 * @return The y location of this entity
	 */
	public int getY() {
		return (int) y;
	}

	public int getScreenX() {
		return screenX;
	}

	public void setScreenX(int screenX) {
		this.screenX = screenX;
	}

	public float getAngel() {
		return angel;
	}

	public void setAngel(float angel) {
		this.angel = angel;
	}

	public int getScreenY() {
		return screenY;
	}

	public void setScreenY(int screenY) {
		this.screenY = screenY;
	}

	public boolean isVisible() {
		return visible;
	}

	public void setVisible(Entity ship) {
		if (!(this instanceof ShipEntity)) {
			setScreenX(getX() - ship.getX() + ship.getScreenX());
			setScreenY(getY() - ship.getY() + ship.getScreenY());
			visible = !(getScreenX() < -sprite.getWidth()
					|| screenX > Game.SCREEN_WIDTH
					|| screenY < -sprite.getHeight() || screenY > Game.SCREEN_HEIGHT);
		}
	}

	/**
	 * Check if this entity collied with another.
	 * 
	 * @param other
	 *            The other entity to check collision against
	 * @return True if the entities collide with each other
	 */
	public boolean collidesWith(Entity other) {
		me.setBounds((int) x, (int) y, sprite.getWidth(), sprite.getHeight());
		him.setBounds((int) other.x, (int) other.y, other.sprite.getWidth(),
				other.sprite.getHeight());

		return me.intersects(him);
	}

	/**
	 * Notification that this entity collided with another.
	 * 
	 * @param other
	 *            The entity with which this entity collided.
	 */
	public abstract void collidedWith(Entity other);

	public double rangeTo(Entity entity) {
		return Math.sqrt((entity.getX() - x) * (entity.getX() - x)
				+ (entity.getY() - y) * (entity.getY() - y));
	}

	public int getMiniMapColor() {
		return miniMapColor;
	}

	public int getSize() {
		return sprite.getHeight();
	}
}